/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       recruit_dialog.cpp

	$Header: /game/recruit_dialog.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "recruit_dialog.h"

#include "abbreviate_number.h"
#include "bitmap_layer_window.h"
#include "bitmap_group_cache.h"
#include "bound_handler.h"
#include "button.h"
#include "button_cache.h"
#include "combat_model_window.h"
#include "creature_traits.h"
#include "format_string.h"
#include "interface_help.h"
#include "item_help_block.h"
#include "material_icons.h"
#include "material_names.h"
#include "replace_keywords.h"
#include "scrollbar.h"
#include "skill_icon.h"
#include "standard_fonts.h"
#include "text_window.h"

static t_bitmap_group_cache g_layout( "dialog.recruit" );

extern t_external_string const k_hotkey_buy;
extern t_button_cache       g_max_button( "button.max" );
extern t_button_cache const k_buy_button;
extern t_button_cache       g_cancel_button;
extern t_bitmap_group_cache g_material_icons;

// ---------------------------------------------------------------------
// recruit creatures dialog
// ---------------------------------------------------------------------
t_recruit_dialog::t_recruit_dialog( t_window* parent, t_creature_type creature_type, 
								    int maximum, t_material_array const& budget, 
									int gold_cost )
				: t_bitmap_layer_window( 0, t_screen_point(0,0) )
{
	initialize( creature_type, maximum, budget, g_layout.get(), gold_cost );
}

// ---------------------------------------------------------------------
// recruit creatures dialog
// ---------------------------------------------------------------------
t_recruit_dialog::t_recruit_dialog( t_window* parent, t_creature_type creature_type,
								    int		  maximum, t_material_array const& budget,
									t_bitmap_group_ptr bitmaps, int gold_cost )
				: t_bitmap_layer_window( 0, t_screen_point(0,0) )
{
	initialize( creature_type, maximum, budget, bitmaps, gold_cost );
}

// ---------------------------------------------------------------------
// add the ability icons
// ---------------------------------------------------------------------
void t_recruit_dialog::add_ability_icons()
{
	// find out how many icons we'll need
	int	i;

	// NOTE: the help balloon text is added inherently by t_skill_icon class methods
	// so we don't have to explicitly add the help here
    for( i=0; i<4; ++i )
    {
        t_screen_rect rect = t_screen_rect(0, 0, 52, 52);
	    t_screen_point point = m_layout->find(format_string( "ability_%i", i+1))->get_rect().top_left();
        rect += point;
		m_ability_icons[i] = new t_skill_icon( rect, this );
    }
}

void t_recruit_dialog::set_ability_icons()
{
	std::vector<t_creature_ability> const & abilities = m_traits->abilities;
	int	i;

    // Clear icons.
    for( i=0; i<4; ++i )
		m_ability_icons[i]->set_ability( k_ability_none );

    for( i=0; i<abilities.size(); ++i )
    {
        if( m_traits->has_ability( abilities[i] ) )
		{
			m_ability_icons[i]->set_ability( abilities[i] );
		}
    }
}

// ---------------------------------------------------------------------
// add a text item at a certain location within a rect
// ---------------------------------------------------------------------
t_text_window* t_recruit_dialog::add_text( std::string const& text, std::string const& layer_name, 
							               int lines, int on_line ) 
{
	t_bitmap_layer const* layer;
	t_screen_rect         rect;
	t_text_window*        text_window;
	t_cached_font         font;
	int                   font_height;

	layer = m_layout->find( layer_name );
	rect = layer->get_rect();
	font_height = rect.height() / lines;
	font = get_font( font_height );
	font_height = font->get_height();
	rect.top += on_line * font_height;
	rect.bottom = rect.top + font_height;
	text_window = new t_text_window( font, rect, this, text, t_pixel_24(0,0,0));
	text_window->set_center_horizontal();
	text_window->set_drop_shadow( true, t_pixel_24(200,200,200));

	return text_window;
}

// ---------------------------------------------------------------------
// add a text item
// ---------------------------------------------------------------------
t_text_window* t_recruit_dialog::add_text( std::string const& text, std::string const& layer_name, 
							               int lines )
{
	t_bitmap_layer const* layer;
	t_screen_rect         rect;
	t_text_window*        text_window;
	t_cached_font         font;

	layer = m_layout->find( layer_name );
	rect = layer->get_rect();
	font = get_font( rect.height() / lines );
	text_window = new t_text_window( font, rect, this, text, t_pixel_24(0,0,0));
	text_window->set_center_horizontal();
	text_window->set_drop_shadow( true, t_pixel_24(200,200,200));
	return text_window;
}

// ---------------------------------------------------------------------
// add an abbreviated number item
// ---------------------------------------------------------------------
t_text_window* t_recruit_dialog::add_number( int number, std::string const& layer_name, int lines )
{
	t_bitmap_layer const* layer;
	t_screen_rect         rect;
	t_text_window*        text_window;
	t_cached_font         font;
	std::string			  text;

	layer = m_layout->find( layer_name );
	rect = layer->get_rect();
	font = get_font( rect.height() / lines );

	text = abbreviate_number( number, *font, rect.width() );

	text_window = new t_text_window( font, rect, this, text, t_pixel_24(0,0,0));
	text_window->set_center_horizontal();
	text_window->set_drop_shadow( true, t_pixel_24(200,200,200));
	return text_window;
}

// ---------------------------------------------------------------------
// add cost icons
// ---------------------------------------------------------------------
void t_recruit_dialog::add_cost_icons( std::string const& base_name, t_window_list&	icons, 
									   std::vector<t_text_window*>& numbers, std::string const& help_name )
{
	t_bitmap_layer const* layer;

	// find out how many icons we'll need
	t_material_array const& cost = m_traits->cost;
	int						count = cost.get_count();
	int						i;
	t_cached_font			font = get_font( 16 );
	t_screen_rect			icon_rect = m_material_icons->get_rect();
	int                     unit_height = font->get_height() + icon_rect.height();
	t_help_block const&     recruit_help = get_help_block( "creature_purchase" );

	// simple stuff - assumes we have 1-3 icons
	assert( count > 0 && count <= 3 );

	// get the resource icon box rects
	t_screen_rect		resource_box_rect[3];

	layer = m_layout->find( base_name + "_1" );
	resource_box_rect[0] = layer->get_rect();

	layer = m_layout->find( base_name + "_2" );
	resource_box_rect[1] = layer->get_rect();

	layer = m_layout->find( base_name + "_3" );
	resource_box_rect[2] = layer->get_rect();

	// get the resource text box rects
	t_screen_rect			resource_text_rect[3];

	layer = m_layout->find( base_name + "_text_1" );
	resource_text_rect[0] = layer->get_rect();

	layer = m_layout->find( base_name + "_text_2" );
	resource_text_rect[1] = layer->get_rect();

	layer = m_layout->find( base_name + "_text_3" );
	resource_text_rect[2] = layer->get_rect();


	t_window*      window;
	t_text_window* text_window;
	t_screen_rect  text_rect;
	t_screen_point point;
	int			   resource_text_index = 0;

	for (i = 0; i < k_material_count; i++)
	{
		if (cost[i] == 0 && i != k_gold)
			continue;

		t_bitmap_layer const* bitmap = m_material_icons->find( k_material_keyword[i] );

		// center the icon inside the icon box
		point.x = resource_box_rect[resource_text_index].left + ( ( resource_box_rect[resource_text_index].width() - bitmap->get_width() ) / 2 );
		point.y = resource_box_rect[resource_text_index].top + ( ( resource_box_rect[resource_text_index].height() - bitmap->get_height() ) / 2 );

		// add icon
		window = new t_bitmap_layer_window( bitmap, point, this );
		set_help( window, recruit_help, help_name );
		std::string text = recruit_help.get_help( help_name );
		text = replace_keywords( text, "%material", k_material_name[i] );
		window->set_right_click_text( text );

		icons.push_back( window );

		// add text for icon
		text_window = new t_text_window( font, resource_text_rect[resource_text_index], this, "", t_pixel_24(0,0,0));
		text_window->set_center_horizontal();
		text_window->set_drop_shadow( true, t_pixel_24(200,200,200));
		set_help( text_window, recruit_help, "total_cost" );
		text_window->set_right_click_text( text );
		numbers.push_back( text_window );
		resource_text_index++;
	}
}

// -----------------------------------------------------------------------
// recruit creatures dialog
// -----------------------------------------------------------------------
void t_recruit_dialog::cancel_clicked( t_button* button )
{
	m_return_value = false;
	close();
}

// ---------------------------------------------------------------------
// create buttons
// ---------------------------------------------------------------------
void t_recruit_dialog::create_buttons()
{
	t_screen_rect rect;
	t_button*     button;
	t_help_block const& help = get_help_block( "creature_purchase" );


	// create maximum button
	//layer = m_layout->find( "max_button" );
	//rect = layer->get_rect();
	//button = new t_button( g_max_button.get(), rect.top_left(), this );
	//button->set_click_handler( bound_handler( *this, &t_recruit_dialog::maximum_clicked ) );
	//set_help( button, help, "hire_all" );

	// create purchase button
	rect = m_layout->find( "buy_button" )->get_rect();
	button = m_purchase_button = new t_button( k_buy_button.get(), rect.top_left(), this );
	button->set_click_handler( bound_handler(*this, &t_recruit_dialog::purchase_clicked ) );
	set_help( button, help, "pay" );	

	// create cancel button
	rect = m_layout->find( "cancel_button" )->get_rect();
	button = new t_button( g_cancel_button.get(), rect.top_left(), this );
	button->set_click_handler( bound_handler(*this, &t_recruit_dialog::cancel_clicked ) );
	set_help( button, help, "cancel" );	
}

// ---------------------------------------------------------------------
// create icons
// ---------------------------------------------------------------------
void t_recruit_dialog::create_icons()
{
	t_bitmap_layer const* layer;
	t_window*             window;
	t_screen_point        point(0,0);
	t_help_block const&   shared_help = get_help_block( "shared" );

	layer = m_layout->find( "damage" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "damage" );

	layer = m_layout->find( "hit_points" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "hit_points" );

	layer = m_layout->find( "movement" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "movement" );

	layer = m_layout->find( "melee_attack" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "attack_skill" );

	layer = m_layout->find( "melee_defense" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "defense_skill" );

	layer = m_layout->find( "speed" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "speed" );

	layer = m_layout->find( "gold" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "gold" );

	layer = m_layout->find( "sulfur" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "sulfur" );

	layer = m_layout->find( "mercury" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "mercury" );

	layer = m_layout->find( "crystal" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "crystal" );

	layer = m_layout->find( "gems" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "gems" );

	layer = m_layout->find( "wood" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "wood" );

	layer = m_layout->find( "ore" );
	window = new t_bitmap_layer_window( layer, point, this );
	set_help( window, shared_help, "ore" );

	//abilities frame
	layer = m_layout->find( "abilities_frame" );
	window = new t_bitmap_layer_window( layer, point, this );

	//animation frame
	layer = m_layout->find( "animation_frame" );
	window = new t_bitmap_layer_window( layer, point, this );
}

// ---------------------------------------------------------------------
// create all the text to go in each text box
// ---------------------------------------------------------------------
static t_external_string const k_text_available( "Available.recruit" );
static t_external_string const k_text_total_cost( "Total_Cost.recruit" );
static t_external_string const k_text_recruit( "Recruit.recruit" );
void t_recruit_dialog::create_text()
{
	// add "available" title
	add_text( k_text_available, "available_number", 2 );

	// add "available" number
	m_available_number = add_text( format_string("%i", m_maximum), "available_number", 2, 1 );
	
	// add "recruit" title
	add_text( k_text_recruit, "recruit_number", 2 );
	m_recruit_number = add_text( "0", "recruit_number", 2, 1 );

	// add "total cost" title
	add_text( k_text_total_cost, "total_cost_text", 1 );

	//
	// show all the creature stats
	//
    t_screen_rect   rect;

	// add "damage" number
	//add_text( format_string( "%i-%i", m_traits->damage_low, m_traits->damage_high ), "damage_text", 1 );
    rect = m_layout->find("damage_text")->get_rect();
	m_damage = new t_text_window( get_font( 14 ), rect, this,
                    "", t_pixel_24(0,0,0) );
    m_damage->set_center_horizontal();
    m_damage->set_center_vertical();

	// add "hitpoints" number
	//add_text( format_string( "%i", m_traits->hit_points ), "hit_points_text", 1 );
    rect = m_layout->find("hit_points_text")->get_rect();
	m_hitpoints = new t_text_window( get_font( 14 ), rect, this,
                    "", t_pixel_24(0,0,0) );
    m_hitpoints->set_center_horizontal();
    m_hitpoints->set_center_vertical();

	// add "movement" number
	//add_text( format_string( "%i", m_traits->movement ), "movement_text", 1 );
    rect = m_layout->find("movement_text")->get_rect();
	m_movement = new t_text_window( get_font( 14 ), rect, this,
                    "", t_pixel_24(0,0,0) );
    m_movement->set_center_horizontal();
    m_movement->set_center_vertical();

	// add "attack skill" number
	//add_text( format_string( "%i", int(m_traits->attack_skill * 10) ), "attack_skill_text", 1 );
    rect = m_layout->find("melee_attack_text")->get_rect();
	m_melee_offense = new t_text_window( get_font( 14 ), rect, this,
                    "", t_pixel_24(0,0,0) );
    m_melee_offense->set_center_horizontal();
    m_melee_offense->set_center_vertical();

	// add "defense skill" number
	//add_text( format_string( "%i", int( m_traits->defense_skill * 10) ), "defense_skill_text", 1 );
    rect = m_layout->find("melee_defense_text")->get_rect();
	m_melee_defense = new t_text_window( get_font( 14 ), rect, this,
                    "", t_pixel_24(0,0,0) );
    m_melee_defense->set_center_horizontal();
    m_melee_defense->set_center_vertical();

	// add "speed" number
	//add_text( format_string( "%i", m_traits->speed ), "speed_text", 1 );
    rect = m_layout->find("speed_text")->get_rect();
	m_speed = new t_text_window( get_font( 14 ), rect, this,
                    "", t_pixel_24(0,0,0) );
    m_speed->set_center_horizontal();
    m_speed->set_center_vertical();

    set_stats();

	//
	// show the player's budget
	//

	// show gold 
	add_number( m_budget[k_gold], "gold_text", 1 );

	// show wood
	add_number(m_budget[k_wood], "wood_text", 1 );

	// show ore
	add_number( m_budget[k_ore], "ore_text", 1 );

	// show crystal
	add_number( m_budget[k_crystal], "crystal_text", 1 );

	// show sulfur
	add_number( m_budget[k_sulfur], "sulfur_text", 1 );

	// show mercury
	add_number( m_budget[k_mercury], "mercury_text", 1 );

	// show gems
	add_number( m_budget[k_gems], "gems_text", 1 );
}

void t_recruit_dialog::set_stats()
{
	m_damage->set_text( format_string( "%i-%i", m_traits->damage_low, m_traits->damage_high ) );
	m_hitpoints->set_text( format_string( "%i", m_traits->hit_points ) );
	m_movement->set_text( format_string( "%i", m_traits->movement ) );
	m_melee_offense->set_text( format_string( "%i", int(m_traits->attack_skill * 10) ));
	m_melee_defense->set_text( format_string( "%i", int( m_traits->defense_skill * 10) ));
	m_speed->set_text( format_string( "%i", m_traits->speed ) );
}

// ---------------------------------------------------------------------
// recruit creatures dialog
// ---------------------------------------------------------------------
void t_recruit_dialog::initialize(	t_creature_type			creature_type, 
									int						maximum,
									t_material_array const&	budget, 
									t_bitmap_group_ptr		bitmaps,
									int						gold_cost )
{
	m_amount = 0;
	m_budget = budget;
	m_layout = bitmaps;
	m_material_icons = g_material_icons.get();
	m_traits = &get_traits( creature_type );
	m_available = maximum;


	//////////////////////////////////////////////////////////////////
 	// find dimensions & center
	t_screen_point        origin(0,0);
	t_bitmap_layer const* layer = m_layout->find( "background" );
	t_screen_rect		  rect = layer->get_rect();

	set_bitmap( layer );
	rect += t_screen_point( (get_parent()->get_width() - rect.width()) / 2, 
		                           (get_parent()->get_height() - rect.height()) / 2 );
	move( rect );
	set_drop_shadow();

	//////////////////////////////////////////////////////////////////

	// add title
	std::string           text;

	layer = m_layout->find( "title" );
	rect = layer->get_rect();
	m_title = new t_text_window( get_font( rect.height() ), rect, this, 
		                         "", t_pixel_24(0,0,0) );
	m_title->set_center_horizontal();
	m_title->set_center_vertical();
	m_title->set_drop_shadow( true, t_pixel_24(200,200,200));

	// add creature display
	rect = m_layout->find( "creature_box" )->get_rect();
	m_creature_display = new t_combat_model_window( rect, this, creature_type );

	add_ability_icons();
	set_ability_icons();
	create_text();
	create_icons();
	create_buttons();

	// create scrollbar
	layer = m_layout->find( "scrollbar" );
	rect  = layer->get_rect();

	m_scrollbar = new t_scrollbar( rect.top_left(), rect.width(), this, 0, 0, false );
	m_scrollbar->set_handler( bound_handler( *this, &t_recruit_dialog::slider_change ));

	set_creature( creature_type, maximum, gold_cost );
}

// ---------------------------------------------------------------------
// recruit creatures dialog
// ---------------------------------------------------------------------
void t_recruit_dialog::maximum_clicked( t_button* button )
{
	m_scrollbar->set_position( m_maximum );
}

// -----------------------------------------------------------------------
// recruit creatures dialog
// -----------------------------------------------------------------------
void t_recruit_dialog::purchase_clicked( t_button* button )
{
	m_return_value = (m_amount > 0);
	close();
}

// ---------------------------------------------------------------------
// set cost windows
// ---------------------------------------------------------------------
void t_recruit_dialog::set_cost_windows( std::vector<t_text_window*>& numbers,
	 		                            t_material_array const& cost )
{
	int count = 0;
	int i;

	for (i = 0; i < k_material_count; i++)
	{
		if (m_cost[i] == 0 && i != k_gold)
			continue;
		assert( count < numbers.size() );
		numbers[count]->set_color( t_pixel_24(0,0,0) );
		numbers[count]->set_text( format_string( "%i", cost[i] ) );
		count++;
	}
}

static t_external_string const k_text_recruit_creatures( "recruit_creatures.recruit" );
static t_external_string const k_text_cost_per( "cost_per.recruit" );

// ---------------------------------------------------------------------
// recruit creatures dialog
// ---------------------------------------------------------------------
void t_recruit_dialog::set_creature( t_creature_type creature_type, int maximum,
									 int gold_cost )
{
	m_traits = &get_traits( creature_type );
	m_maximum = maximum;
	m_available = m_maximum;
	m_cost = m_traits->cost;
	if (gold_cost >= 0)
		m_cost[k_gold] = gold_cost;
	m_creature_type = creature_type;
	m_amount = 0;

	t_screen_rect rect;

	// add title
	std::string           text;
	std::string			  name = m_traits->plural_name;
	std::string::iterator iter;

	std::transform( name.begin(), name.begin()+1, name.begin(), toupper );
	for( iter = name.begin(); iter != name.end(); ++iter )
	{

		if( (*iter) == ' ' )
		{
			if( (iter+1) != name.end() )
				std::transform( iter+1, iter+2, iter+1, toupper );
		}
	}

	text = replace_keywords( k_text_recruit_creatures, "%creatures", name );
	// make it all upper case
	m_title->set_text( text );

	m_creature_display->set( creature_type );

	// update the available count
	m_available_number->set_text( format_string("%i", m_maximum) );
    set_stats();
	set_ability_icons();

	// limit maximum to what we can afford
	int minimum = 1;

	maximum = m_budget/ m_cost;
	if (maximum < m_maximum)
		m_maximum = maximum;
	if (minimum > maximum)
		minimum = maximum;
	m_amount = m_maximum;

	// add "recruit" title
	m_recruit_number->set_text( format_string( "%i", m_amount ) );

	// clear old cost icons / numbers
	int						i;
	t_window_list::iterator index;
	
    for (index = m_cost_icons.begin(); index != m_cost_icons.end(); index++)
		index->get()->close();
	m_cost_icons.clear();
	for (i = 0; i < m_cost_numbers.size(); i++)
		m_cost_numbers[i]->close();
	m_cost_numbers.clear();

	add_cost_icons( "individual_resource", m_cost_icons, m_cost_numbers, "unit_cost" );
	set_cost_windows( m_cost_numbers, m_cost );

	for (index = m_total_cost_icons.begin(); index != m_total_cost_icons.end(); index++)
		index->get()->close();
	m_total_cost_icons.clear();
	for (i = 0; i < m_total_cost_numbers.size(); i++)
		m_total_cost_numbers[i]->close();
	m_total_cost_numbers.clear();

	// add "total cost" icons
	t_material_array total = m_cost;

	total *= m_amount;
	add_cost_icons( "resource", m_total_cost_icons, m_total_cost_numbers, "total_cost" );
	set_cost_windows( m_total_cost_numbers, total );

	// update scrollbar
	m_scrollbar->set_visible( m_amount > 0 );
    if (m_amount > 0)
    {
    	m_scrollbar->set_limits( minimum, m_maximum );
    	m_scrollbar->set_position( m_maximum );
    	m_scrollbar->set_page_size( (m_maximum + 4) / 5 );
    }
	
    m_purchase_button->enable( ((m_available > 0 ) && (m_amount > 0)) );
}

// ---------------------------------------------------------------------
// recruit creatures dialog
// ---------------------------------------------------------------------
void t_recruit_dialog::slider_change( t_scrollbar* scrollbar, int value )
{
	m_recruit_number->set_text( format_string( "%i", value ));

	// compuzte total cost
	t_material_array total = m_cost;

	m_amount = value;
	total *= m_amount;
	set_cost_windows( m_total_cost_numbers, total );
    m_purchase_button->enable( ((m_available > 0 ) && (m_amount > 0)) );
}

